package com.jichangxiu.system.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.entity.rule.*;
import com.jichangxiu.common.utils.JcxContextUtils;
import com.jichangxiu.common.utils.ServletUtils;
import com.jichangxiu.framework.entity.bo.SecurityPost;
import com.jichangxiu.framework.entity.bo.SecurityPower;
import com.jichangxiu.framework.entity.bo.SecurityRole;
import com.jichangxiu.framework.entity.bo.SecurityUser;
import com.jichangxiu.framework.service.AuthUserDetailsService;
import com.jichangxiu.framework.utils.DynamicUtils;
import com.jichangxiu.system.entity.vo.SysTenantVo;
import com.jichangxiu.system.entity.vo.SysUserVo;
import com.jichangxiu.system.service.SysTenantService;
import com.jichangxiu.system.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * AuthUserDetailsServiceImpl:
 *
 * @author Jcx
 * @create 2024-06-29 13:49
 */
@Service
public class AuthUserDetailsServiceImpl implements AuthUserDetailsService {

    private final SysUserService sysUserService;

    private final SysTenantService sysTenantService;

    @Autowired
    public AuthUserDetailsServiceImpl(SysUserService sysUserService, SysTenantService sysTenantService) {
        this.sysUserService = sysUserService;
        this.sysTenantService = sysTenantService;
    }

    /**
     * 通过用户基础信息查找用户
     *
     * @param tenantId 租户 Id
     * @param userInfo 用户基础信息 用户账户 / 用户手机号 / 用户邮箱
     * @return 用户信息
     * @throws UsernameNotFoundException 不存在异常
     */
    @Override
    public UserDetails loadUserByTenantAndUserInfo(String tenantId, String userInfo) throws UsernameNotFoundException {
        // 租户信息
        SysTenantVo sysTenantVo = sysTenantService.info(tenantId, false);
        if (ObjectUtil.isEmpty(sysTenantVo)) throw new UsernameNotFoundException("没有查询到指定租户");
        if (ObjectUtil.isEmpty(sysTenantVo.getSourceCode())) throw new UsernameNotFoundException("租户未配置数据源");
        // 当前租户如果能拿到的话，就先将租户信息设置到线程数据中去
        JcxContextUtils.fillJcxContextEntityByTenant(sysTenantVo.getId(), sysTenantVo.getName(), sysTenantVo.getSourceCode());
        TenantRule tenantRule = TenantRule.builder().tenantId(sysTenantVo.getId()).tenantCode(sysTenantVo.getCode()).tenantName(sysTenantVo.getName()).isPlatform(sysTenantVo.getPlatform()).tenantSource(sysTenantVo.getSourceCode()).build();

        // 切换为租户数据源
        DynamicUtils.changeSource(sysTenantVo.getSourceCode());
        // 用户信息
        // mybatis-plus 会自动加上 tenant_id = ?
        SysUserVo sysUserVo = sysUserService.infoByLoginInfo(userInfo);
        if (ObjectUtil.isEmpty(sysUserVo))
            throw new UsernameNotFoundException("根据 " + userInfo + " 查询不到指定用户");
        UserRule userRule = UserRule.builder().userId(sysUserVo.getId()).userCode(sysUserVo.getCode()).nickName(sysUserVo.getName()).loginName(sysUserVo.getUsername()).loginPwd(sysUserVo.getPassword()).deptId(sysUserVo.getDeptId()).deptName(sysUserVo.getDeptName()).build();
        // 如果能拿到用户信息，那么就记录用户信息到当前线程数据
        JcxContextUtils.fillJcxContextEntityByUser(sysUserVo.getDeptId(), sysUserVo.getDeptName(), sysUserVo.getId(), sysUserVo.getCode(), sysUserVo.getName());
        // 从租户的库里面查到了指定的用户，后重置数据源
        DynamicUtils.resetSource();

        // 岗位相关填充
        List<SecurityPost> securityPostList = new ArrayList<>();
        List<PostRule> postRuleList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(sysUserVo.getSysPostVoList())) {
            sysUserVo.getSysPostVoList().forEach(sysPostVo -> {
                PostRule postRule = PostRule.builder().postId(sysPostVo.getId()).postCode(sysPostVo.getCode()).postName(sysPostVo.getName()).postScope(sysPostVo.getScope()).build();
                securityPostList.add(new SecurityPost(postRule));
                postRuleList.add(postRule);
            });
        }
        // 权限相关填充
        List<SecurityPower> securityPowerList = new ArrayList<>();
        List<PowerRule> powerRuleList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(sysUserVo.getSysPowerVoList())) {
            sysUserVo.getSysPowerVoList().forEach(sysPowerVo -> {
                PowerRule powerRule = PowerRule.builder().powerId(sysPowerVo.getId()).powerCode(sysPowerVo.getCode()).build();
                securityPowerList.add(new SecurityPower(powerRule));
                powerRuleList.add(powerRule);
            });
        }
        if ((Constants.TEN_SUPER_USER.equals(sysUserVo.getCode()) || Constants.SYS_SUPER_USER.equals(sysUserVo.getCode())) && securityPowerList.stream().noneMatch(securityPower -> Constants.ALL_PERMISSION.equals(securityPower.getPowerCode()))) {
            // 如果是平台超级管理员 和 租户超级管理员 添加顶级权限
            PowerRule powerRule = PowerRule.builder().powerId(IdWorker.getIdStr()).powerCode(Constants.ALL_PERMISSION).build();
            securityPowerList.add(new SecurityPower(powerRule));
            powerRuleList.add(powerRule);
        }
        // 角色相关填充
        List<SecurityRole> securityRoleList = new ArrayList<>();
        List<RoleRule> roleRuleList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(sysUserVo.getSysRoleVoList())) {
            sysUserVo.getSysRoleVoList().forEach(sysRoleVo -> {
                RoleRule roleRule = RoleRule.builder().roleId(sysRoleVo.getId()).roleCode(sysRoleVo.getCode()).roleName(sysRoleVo.getName()).build();
                securityRoleList.add(new SecurityRole(roleRule));
                roleRuleList.add(roleRule);
                // 如果有超级管理员角色，也可以拥有顶级权限
                if ((Constants.TEN_SUPER_ROLE.equals(sysRoleVo.getCode()) || Constants.SYS_SUPER_ROLE.equals(sysRoleVo.getCode())) && securityPowerList.stream().noneMatch(securityPower -> Constants.ALL_PERMISSION.equals(securityPower.getPowerCode()))) {
                    PowerRule powerRule = PowerRule.builder().powerId(IdWorker.getIdStr()).powerCode(Constants.ALL_PERMISSION).build();
                    securityPowerList.add(new SecurityPower(powerRule));
                    powerRuleList.add(powerRule);
                }
            });
        }
        JcxContextUtils.fillJcxContextEntityByAuth(postRuleList, roleRuleList, powerRuleList);
        return SecurityUser.builder().tenantRule(tenantRule).userRule(userRule).securityPostList(securityPostList).securityRoleList(securityRoleList).securityPowerList(securityPowerList).build();
    }

    /**
     * 通过用户基础信息查找用户
     *
     * @param userInfo 用户基础信息 用户账户 / 用户手机号 / 用户邮箱
     * @return 用户信息
     * @throws UsernameNotFoundException 不存在异常
     */
    @Override
    public UserDetails loadUserByUsername(String userInfo) throws UsernameNotFoundException {
        // 必须在 com.jichangxiu.framework.filter.LoginFilter.attemptAuthentication 中向请求中设置租户
        String tenantId = ServletUtils.getInfoFromRequest(ServletUtils.getRequest(), Constants.JCX_PROPERTIES.getMyBatisPlusProperties().getTenantHeaderKey(), String.class);
        return loadUserByTenantAndUserInfo(tenantId, userInfo);
    }

}
